﻿using System;
using System.IO.Ports;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
using OpenTap.Plugins.Interfaces.LedAnalyzer;

namespace OpenTap.Plugins.LedAnalyzer
{
    [Display("FeasaLedAnalyzer", Group: "LED Analyzer", Description: "Feasa LED Analyzer driver")]
    public class FeasaLedAnalyzer : Instrument, ILedAnalyzer
    {

        #region Settings
        [Display("ComPort", Description: "The serial port ID of Feasa", Order: 1)]
        [AvailableValues("AvailablePorts")]
        public string ComPort { get; set; }
        [Display("BaudRate", Description: "The serial port BaudRate of Feasa ", Order: 2)]
        public EBaudRate BaudRate { get; set; }
        [Display("DataBits", Description: "The serial port DataBits of Feasa ", Order: 3)]
        public EDataBits DataBits { get; set; }
        [Display("Parity", Description: "The serial port Parity of Feasa ", Order: 4)]
        public Parity ParityCfg { get; set; }
        [Display("StopBits", Description: "The serial port StopBits of Feasa ", Order: 5)]
        public StopBits StopBitsCfg { get; set; }
        [Display("TimeOutMs", Description: "The serial port TimeOut Ms ", Order: 7)]
        public int TimeOutMs { get; set; }
        #endregion
        protected bool IsInitialized;
        private SerialPort _serialConnection;
        public virtual SerialPort FeasaSerial
        {
            get { return _serialConnection ?? (_serialConnection = new SerialPort()); }
        }

        [Browsable(false)]
        public string[] AvailablePorts { get; set; }

        public FeasaLedAnalyzer()
        {
            AvailablePorts = SerialPort.GetPortNames();
            TimeOutMs = 800;
            Name = "Feasa";
        }
        public override void Open()
        {
            base.Open();
            Initialize();
        }
        public override void Close()
        {
            if (IsInitialized)
            {
                FeasaSerial.Close();
                IsInitialized = false;
            }
            Log.Info("Feasa led Sensor closed!");
            base.Close();
        }
        public void Initialize()
        {
            if (IsInitialized) throw new InvalidOperationException("Feasa led sensor is already initialized!");
            Log.Info("Initializing Feasa led Sensor....");
            try
            {
                FeasaSerial.PortName = ComPort;
                FeasaSerial.BaudRate = (int)BaudRate;
                FeasaSerial.Parity = ParityCfg;
                FeasaSerial.DataBits = (int)DataBits;
                FeasaSerial.StopBits = StopBitsCfg;
                FeasaSerial.ReadTimeout = TimeOutMs;
                FeasaSerial.Open();
            }
            catch (Exception ex)
            {
                Log.Error("Initializing Feasa led sensor failed: " + ex);
                IsInitialized = false;
                throw;
            }

            IsInitialized = true;
            Log.Info("Initializing Feasa led sensor done.");
        }

        public void CaptureData(EBrightnessLevel brightnessLevel)
        {
            Log.Info("Feasa capture LED data start.");
            string command;
            if (brightnessLevel == EBrightnessLevel.Auto)
                command = "capture";
            else
                command = "capture" + (int)brightnessLevel;
            string response;
            try
            {
                response = ExecuteFeasaCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Capture Feasa Data failed: " + ex);
                throw;
            }
            if (!response.EndsWith("OK\r\n", StringComparison.InvariantCultureIgnoreCase))
                throw new ApplicationException("Capture command error");
        }
        public void CaptureDataPwm(EBrightnessLevel brightnessLevel, int averageFactor = 0)
        {
            if (averageFactor > 15 || averageFactor < 0)
                throw new ArgumentException("AverageFactor should be between 1~15");

            Log.Info( "Feasa capture LED PWM data start.");
            var command = "capture";
            if (brightnessLevel != EBrightnessLevel.Auto)
                command = command + (int)brightnessLevel;

            command = command + "pwm";
            if (averageFactor != 0)
                command = command + String.Format("{0:D2}", averageFactor);

            var response = "";
            try
            {
                response = ExecuteFeasaCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Capture Feasa Data Pwm failed: " + ex);
                //IsInitialized = false;
            }
            if (!response.EndsWith("OK\r\n", StringComparison.InvariantCultureIgnoreCase))
                throw new ApplicationException("Capture command error");
        }
        public RgbData GetRgb(int channel)
        {
            if (channel <= 0 || channel > 20)
                throw new ArgumentException("Channel of fiber should be 1~20");

            Log.Info( "Feasa read LED RGB result.");
            var command = "getrgbi" + $"{channel:D2}";
            string response;
            try
            {
                response = ExecuteFeasaCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Get Feasa Rgb Data failed: " + ex);
                //IsInitialized = false;
                throw;
            }
            var respArray = response.Split(' ');
            var rgbData = new RgbData
            {
                Red = Convert.ToByte(respArray[0], CultureInfo.InvariantCulture),
                Green = Convert.ToByte(respArray[1], CultureInfo.InvariantCulture),
                Blue = Convert.ToByte(respArray[2], CultureInfo.InvariantCulture)
            };
            Log.Info("Red: " + rgbData.Red + ", Green: " + rgbData.Green + ", Blue: " + rgbData.Blue + ".");
            return rgbData;
        }

        public List<RgbData> GetRgbs()
        {
            throw new NotImplementedException();
        }

        public HsiData GetHsi(int channel)
        {
            if (channel <= 0 || channel > 20)
                throw new ArgumentException("Channel of fiber should be 1~20");

            Log.Info( "Feasa read LED HSI result.");
            var command = "gethsi" + String.Format("{0:D2}", channel);
            string response;
            try
            {
                response = ExecuteFeasaCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Get Feasa Hsi Data failed: " + ex);
                //IsInitialized = false;
                throw;
            }
            var respArray = response.Split(' ');
            var hsiData = new HsiData
            {
                Hue = Convert.ToDouble(respArray[0], CultureInfo.InvariantCulture),
                Saturation = Convert.ToInt32(respArray[1], CultureInfo.InvariantCulture),
                Intensity = Convert.ToInt32(respArray[2], CultureInfo.InvariantCulture)
            };
            Log.Info( "Hue: " + hsiData.Hue + ", Saturation: " +
                hsiData.Saturation + ", Intensity: " + hsiData.Intensity + ".");
            return hsiData;
        }
        public ChromaticityData GetChromaticity(int channel)
        {
            if (channel <= 0 || channel > 20)
                throw new ArgumentException("Channel of fiber should be 1~20");

            Log.Info( "Feasa read LED chromaticity result.");
            var command = "getxy" + String.Format("{0:D2}", channel);
            string response;
            try
            {
                response = ExecuteFeasaCommand(command, TimeOutMs);
            }
            catch (Exception ex)
            {
                Log.Error("Get Feasa Chromaticity Data failed: " + ex);
                //IsInitialized = false;
                throw;
            }
            var respArray = response.Split(' ');
            var chromaticityData = new ChromaticityData
            {
                XChromaticity = Convert.ToDouble(respArray[0], CultureInfo.InvariantCulture),
                YChromaticity = Convert.ToDouble(respArray[1], CultureInfo.InvariantCulture),
            };
            Log.Info( "Chromaticity X: " + chromaticityData.XChromaticity +
                ", Chromaticity Y: " + chromaticityData.YChromaticity + ".");
            return chromaticityData;
        }
        private string ExecuteFeasaCommand(string command, int timeOutMs)
        {
            if (FeasaSerial == null || !IsInitialized)
                    throw new ApplicationException("Rs232 interface not initialized");
            if (!command.EndsWith("\r\n"))
                command = command + "\r\n";
            Log.Info( "Feasa command: " + command.Substring(0, command.Length - 2));
            FeasaSerial.DiscardInBuffer();
            FeasaSerial.DiscardOutBuffer();
            FeasaSerial.Write(command);
            var response = "";
            var recTimer = new Stopwatch();
            recTimer.Start();
            while (true)
            {
                response = response + FeasaSerial.ReadExisting();
                if (response.EndsWith("\r\n"))
                {
                    Log.Info( "Feasa response: " + response.Substring(0, response.Length - 2));
                    return response;
                }
                if (recTimer.ElapsedMilliseconds > timeOutMs)
                    throw new ApplicationException("Feasa command " + command + " timeout.");
            }
        }

        public List<HsiData> GetHsiGroup(int channelgroup)
        {
            throw new NotImplementedException();
        }

        public void Initialize(JObject configElement)
        {
            throw new NotImplementedException();
        }
    }
}
